#pragma once

#include <iostream>
#include <string>
#include <queue>
#include <vector>
#include <memory>
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "thread.hpp"

namespace ThreadPoolModule
{
    using namespace lyrics;
    using namespace ThreadModule;
    using namespace LockModule;
    using namespace CondModule;

    // 用来做测试的线程方法
    void DefaultTest()
    {
        while (true)
        {
            LOG(LogLevel::DEBUG) << "我是一个测试方法";
            sleep(1);
        }
    }

    using thread_t = std::shared_ptr<Thread>;

    const static int defaultnum = 5;

    template <typename T>
    class ThreadPool
    {
    private:
        bool IsEmpty(){return _taskq.empty();}

        //将名字传入HandlerTask的逻辑当中
        void HandlerTask(const std::string name)
        {
            LOG(LogLevel::INFO) << "线程" << name <<", 进入handlerTask的逻辑";
            while(true)
            {
                //1.拿任务
                T t;
                {
                    LockGuard lockguard(_mutex);
                    while(IsEmpty() && _isrunning)//当任务队列不为空并且isrunning是true的时候是可以运行的
                    {
                        _wait_num++;
                        //到指定条件变量下休眠
                        _cond.Wait(_mutex);
                        _wait_num--;
                    }

                    //当任务队列为空，并给线程池退出了，就直接break
                    if(IsEmpty() && !_isrunning) 
                    {
                        break;
                    }
                    //任务队列不为空 && 线程池退出了
                    t = _taskq.front();
                    _taskq.pop();
                }
                //2.处理任务----处理任务的部分是否需要在锁之下进行？-----答案是不需要的，处理任务期间可以直接将锁释放
                t(name);//规定未来所有的任务处理，全部必须提供()方法
            }
            LOG(LogLevel::INFO) << "线程：" << name << "退出";
        }
        
        //将拷贝构造函数禁用
        ThreadPool(const ThreadPool<T> &) = delete;
        //赋值拷贝函数
        ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;
        //线程池默认是关闭的工作状态-----构造函数也需要设置为私有
        ThreadPool(int num = defaultnum):_num(num),_wait_num(0),_isrunning(false)
        {
            for(int i = 0;i < _num;i++)
            {
                //把Threadpool的函数方法绑定给Thread类，那么Thread类就可以执行Handler方法了 
                _threads.push_back(std::make_shared<Thread>(std::bind(&ThreadPool::HandlerTask,this,std::placeholders::_1)));
                LOG(LogLevel::INFO) << "创建线程" << _threads.back()->Name() << "创建成功";
            }
        }
    public:
        static ThreadPool<T> *getInstance()
        {
            //只有第一次会加锁
            if(instance == NULL)
            {
                //加锁
                LockGuard lockguard(_lock);
                if(instance == NULL)
                {
                    instance = new ThreadPool<T>();
                }
            }
            return instance;
        }
        void Equeue(T &&in)
        {
            //加锁
            LockGuard lockgaurd(_mutex);
            if(!_isrunning) return;
            //这里用move不需要拷贝
            _taskq.push(std::move(in));
            if(_wait_num > 0)
            {
                //唤醒线程
                _cond.Notify();
            }
        }
        void Start()
        {
            //如果线程已经启动，直接返回
            if(_isrunning) return;
            _isrunning = true;
            for (auto &thread_ptr : _threads)
            {
                LOG(LogLevel::DEBUG) << "启动线程" << thread_ptr->Name() << "对象成功";
                thread_ptr->Start();
            }
        }
        void Wait()
        {
            for (auto &thread_ptr : _threads)
            {
                LOG(LogLevel::DEBUG) << "回收线程" << thread_ptr->Name() << " ... 成功";
                thread_ptr->Join();
            }
        }
        //线程池如何Stop
        void Stop()
        {
            //加锁
            LockGuard lockguard(_mutex);
            //只有线程正在运行的时候才能stop
            if(_isrunning)
            {
                //线程池处于不工作状态
                _isrunning = false;
                //1. 让线程自己退出（要唤醒）----如何让线程自己退出----任务队列为空&&is running为false
                if(_wait_num > 0) _cond.NotifyAll();//唤醒所有线程---处理历史任务
                //2. 历史的任务被处理完了
                //3. 不能入任务了---stop之后不能入任务了

            }
        }
        ~ThreadPool()
        {}

    private:
        std::vector<thread_t> _threads;
        int _num;
        int _wait_num;
        std::queue<T> _taskq; // 临界资源
        Mutex _mutex;
        Cond _cond;
        bool _isrunning;

        //定义一个指针
        static ThreadPool<T> *instance;//静态指针的初始化需要放在类外面
        static Mutex _lock;//只用于保护单例
    };
    //初始化静态指针变量
    template<class T>
    ThreadPool<T> *ThreadPool<T>::instance = NULL;
}